/*
    SVN Graph Branches
    Copyright © 2010 Olivier Mengué

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as
    published by the Free Software Foundation, either version 3 of the
    License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.sax.TransformerHandler;
import javax.xml.transform.sax.SAXTransformerFactory;
import javax.xml.transform.sax.SAXSource;
import javax.xml.transform.sax.SAXResult;
import org.xml.sax.XMLReader;
import org.xml.sax.InputSource;
import org.xml.sax.helpers.XMLReaderFactory;
import javax.xml.transform.stream.StreamSource;
import javax.xml.transform.stream.StreamResult;
import java.io.File;
import java.io.FileInputStream;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
import java.util.Map;
import java.util.HashMap;
import java.util.ArrayList;
import java.util.List;
import java.util.Iterator;

class XSLTPipe {

	public static final String USAGE =
		"usage: java XSLTPipe <trans1.xslt> [args1] [<trans2.xslt> [args2] [...]] input.xml";

	private static void usage(int errCode, String message)
	{
		if (message != null)
			System.err.println("XSLTPipe: "+message);
		System.out.println(
			USAGE.replaceAll("\n", System.getProperty("line.separator"))
		);
		System.exit(errCode);
	}

	public static void usage()
	{
		usage(1, null);
	}


	private static final Pattern ARG_PATTERN = Pattern.compile("^--([%!+]?)([^=]+)(=(.*))?$");
    // Input for readTransformerParams
    // This is really dirty but it avoids to create a new storage class
	private static String[] args;
	private static int argsIndex;

	private static Map<String, Object> parseTransformerParams()
	{
		Map<String, Object> params = new HashMap<String, Object>();
		String valueStr;
		String name;

		while (argsIndex < args.length) {
			Matcher m = ARG_PATTERN.matcher(args[argsIndex]);
			if (!m.matches())
				break;
			argsIndex++;
			// TODO check for duplicates
			name = m.group(2);
			if (params.containsKey(name))
				usage(1, "param \""+name+"\" already set");
			if (m.group(3) == null)
				valueStr = args[argsIndex++];
			else
				valueStr = m.group(4);
			String sigil = m.group(1);
			Object value = "";
			if ("!".equals(sigil)) {
				value = Boolean.valueOf(valueStr);
			} else if ("%".equals(sigil)) {
				value = valueStr;
			} else if ("+".equals(sigil)) {
				if (valueStr.indexOf('.') >= 0)
					value = Float.valueOf(valueStr);
				else
					value = Integer.valueOf(valueStr);
			} else {
				value = valueStr;
			}
			params.put(name, value);
			//System.err.println(name+": "+value.toString());
		}
		return params;
	}

    // Output variables for parseArgs()
	// This is really dirty, but it avoid creating more than one class
	private static final List<File>                transformers = new ArrayList<File>();
	private static final List<Map<String, Object>> transformersParams = new ArrayList<Map<String, Object>>();

	private static void parseArgs(String[] cmdArgs)
	{
		args = cmdArgs;
		argsIndex = 0;
		if (args.length < 2)
			usage(1, "missing arguments");
		if (args[0].startsWith("--"))
			usage();

		int lastArgIdx = args.length - 1;
		while (argsIndex < lastArgIdx) {
			String t = args[argsIndex++];
			transformers.add(new File(t));
			transformersParams.add(parseTransformerParams());
		}
		if (argsIndex != lastArgIdx || args[argsIndex].startsWith("--"))
			usage(1, "missing input file");
	}


	public static void main(String[] args) throws Exception
	{
		TransformerFactory tFactory = TransformerFactory.newInstance();
		if ( !tFactory.getFeature(SAXSource.FEATURE) || !tFactory.getFeature(SAXResult.FEATURE) ) {
			System.err.println("missing SAX features in the XML implementation of the JVM");
			System.exit(1);
		}

		parseArgs(args);

		// http://www.stylusstudio.com/xsllist/200308/post80450.html
		SAXTransformerFactory saxTFactory = (SAXTransformerFactory) tFactory;

		TransformerHandler trans[] = new TransformerHandler[transformers.size()];
		for(int i=0; i<trans.length; i++) {
			TransformerHandler t = saxTFactory.newTransformerHandler(new StreamSource(transformers.get(i)));
			Transformer tr = t.getTransformer();
			Iterator<Map.Entry<String, Object>> it = transformersParams.get(i).entrySet().iterator();
			while (it.hasNext()) {
				Map.Entry<String, Object> e = it.next();
				tr.setParameter(e.getKey(), e.getValue());
			}
			trans[i] = t;
			// Connect the pipe
			if (i > 0)
				trans[i-1].setResult(new SAXResult(t));
		}

		// Pipe output
		trans[trans.length-1].setResult(new StreamResult(System.out));

		// Feed the pipe
		XMLReader reader = XMLReaderFactory.createXMLReader();
		reader.setContentHandler(trans[0]);
		reader.setProperty("http://xml.org/sax/properties/declaration-handler", trans[0]);
		reader.setProperty("http://xml.org/sax/properties/lexical-handler", trans[0]);
		reader.parse(new InputSource(new FileInputStream(args[args.length-1])));
	}
}

// vim:set ts=4 sw=4 sts=4:
